package net.sf.cldsql.dj.db;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.sf.cldsql.dj.utl.DiException;
import net.sf.cldsql.sql2json.main.SjConf;
import net.sf.cldsql.sql2json.main.SjServlet;


/**
 * ConnPool
 */
public class ConnPool {
    
    final Logger log = LoggerFactory.getLogger(SjServlet.class);
    
    private String name;
    private String driver;
    private String url;
    private String username;
    private String password;
    private int maxConn;
    
    private final ArrayList<Connection> idleConns = new ArrayList<Connection>();

    /** @return name */
    public String getName() {
        return name;
    }

    public ConnPool (String name, String driver, String url, String username, String password, int maxConn) {
        log.info("ConnPool {} start with {} to {} for {} has maxConn {}.", name, driver, url, username, maxConn);
        this.name = name;
        this.driver = driver;
        this.url = url;
        this.username = username;
        this.password = password;
        this.maxConn = maxConn;
        startup();
        log.info("ConnPool {} initialized.", name);
    }
    
    public void startup() {
        try {
            Class.forName(driver);
            
            // add to pool under sync mode
            synchronized (this) {
                for (int i = 0; i < maxConn; i ++) {
                    log.debug("Create conn #{}.", i);
                    Connection conn = DriverManager.getConnection(
                            url,
                            username, 
                            password);
                    conn.setAutoCommit(false);
                    idleConns.add(conn);
                }
            }
        } catch (SQLException e) {
            throw new DiException(e);
        } catch (ClassNotFoundException e) {
            throw new DiException(e);
        }
    }
    
    public void shutdown() {
        log.info("ConnPool {} shutdown start.", name);
        for (int i = 0; i < idleConns.size(); i ++) {
            Connection conn = idleConns.get(i);
            try {
                conn.close();
                idleConns.remove(i);
            } catch (SQLException e) {
                throw new DiException(e);
            }
        }
        log.info("ConnPool {} shutdown finished.", name);
    }
    
    /**
     * @return connection
     */
    public Connection get() {
        // TODO timeout
        while (true) {
            if (idleConns.size() == 0) {
                try {
                    log.debug("ConnPool {} start wait for no more idle conn.", name);
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new DiException(e);
                }
            } else {
                // remove from idle pool under sync mode
                synchronized (this) {
                    if (idleConns.size() != 0) {
                        Connection conn = idleConns.get(idleConns.size() - 1);
                        idleConns.remove(idleConns.size() - 1);
                        log.debug("ConnPool {} get conn {} .", name, conn);
                        return conn;
                    }
                }
            }
        }
    }
    
    /**
     * @param conn
     */
    public void release (Connection conn) {
        // release and add to pool
        synchronized (this) {
            log.debug("ConnPool {} release conn {} .", name, conn);
            idleConns.add(conn);
        }
    }
    
    protected ArrayList<Connection> getIdleConns() {
        return idleConns;
    }
    
    public void finalize () {
        shutdown ();
    }
}
